package gang.org.springframework.framework.metadata;


import gang.org.springframework.framework.util.GangClassUtils;
import gang.org.springframework.framework.util.GangReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.function.Function;

/**
 * @author gang.chen
 * @since 2021/6/30 8:00
 */
final public class GangTypeMappedAnnotation<A extends Annotation> extends GangAbstractMergedAnnotation<A>
{
    private final GangAnnotationTypeMapping mapping;
    private final ClassLoader classLoader;
    private final Object source;
    private final Object rootAttributes;
    private final GangValueExtractor valueExtractor;
    private final int aggregateIndex;
    private final int[] resolvedRootMirrors;

    private final boolean useMergedValues;

    /**
     * @param mapping {
     *                mapping.annotationType = Import
     * }
     *
     * @param annotation {
     *                annotation.source = ServletWebServerFactoryAutoConfiguration
     * }
     * */
    static <A extends Annotation> GangTypeMappedAnnotation<A> createIfPossible(GangAnnotationTypeMapping mapping,
                                                                               GangMergedAnnotation<?> annotation,
                                                                               Object logger)
    {

        return null;
    }

    /**
     * @param mapping {@link GangAnnotationTypeMapping}
     * @param source {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * @param annotation {@link gang.org.springframework.boot.autoconfigure.GImport}
     * @param aggregateIndex value is 0
     * @param logger TODO
     * */
    static <A extends Annotation> GangTypeMappedAnnotation<A> createIfPossible(GangAnnotationTypeMapping mapping,
                                                                               Object source,
                                                                               Annotation annotation,
                                                                               int aggregateIndex,
                                                                               Object logger)
    {
        return createIfPossible(mapping,source,annotation, GangReflectionUtils::invokeMethod,aggregateIndex,null);
    }

    private static <A extends Annotation> GangTypeMappedAnnotation<A> createIfPossible(GangAnnotationTypeMapping mapping,
                                                                                       Object source,
                                                                                       Object rootAttribute,
                                                                                       GangValueExtractor valueExtractor,
                                                                                       int aggregateIndex,
                                                                                       Object logger)
    {
        return new GangTypeMappedAnnotation<>(mapping,null,source,rootAttribute,valueExtractor,aggregateIndex);
    }

    private GangTypeMappedAnnotation(GangAnnotationTypeMapping mapping,
                                     ClassLoader classLoader,
                                     Object source,
                                     Object rootAttributes,
                                     GangValueExtractor valueExtractor,
                                     int aggregateIndex){
        this(mapping,classLoader,source,rootAttributes,valueExtractor,aggregateIndex,null);
    }


    private GangTypeMappedAnnotation(GangAnnotationTypeMapping mapping,
                                     ClassLoader classLoader,
                                     Object source,
                                     Object rootAttributes,
                                     GangValueExtractor valueExtractor,
                                     int aggregateIndex,
                                     int[] resolveRootMirrors)
    {
        this.mapping = mapping;
        this.classLoader = classLoader;
        this.source = source;
        this.rootAttributes = rootAttributes;
        this.valueExtractor = valueExtractor;
        this.aggregateIndex = aggregateIndex;
        this.resolvedRootMirrors = resolveRootMirrors;
        this.useMergedValues = true;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class<A> getType() {
        return (Class<A>)this.mapping.getAnnotationType();
    }

    /**
     * @param factory 可传入一个lambda表达式 {@link GangAnnotationAttributes}
     * */
    @Override
    public <T extends Map<String, Object>> T asMap(Function<GangMergedAnnotation<?>, T> factory, GangAdapt... adaptations) {
        /**
         * 业务意义，返回一个GangAnnotationAttributes map 对象
         * */
        T map = factory.apply(this);

        GangAttributeMethods attributes = this.mapping.getAttributes();

        for (int i = 0; i < attributes.size(); i++) {
            Method attribute = attributes.get(i);
            //value -> String[]
            Object value = getValue(i,getTypeForMapOptions(attribute,adaptations));

            if(value != null){
                map.put(attribute.getName(),adaptValueForMapOptions(attribute,value,null,null,null));
            }
        }
        return map;
    }

    //TODO
    private <T extends Map<String,Object>> Object adaptValueForMapOptions(Method attribute,
                                                                          Object value,
                                                                          Class<?> mapType,
                                                                          Function<GangMergedAnnotation<?>,T> factory,
                                                                          GangAdapt[] adaptations)
    {
        return value;
    }

    //String[]
    private <T> T getValue(int attributeIndex, Class<T> type)
    {
        Method attribute = this.mapping.getAttributes().get(attributeIndex);
        Object value = getValue(attributeIndex, true, true);
        if(value == null){
            //TODO
        }
        return adapt(attribute,value,type);
    }

    private Object getValue(int attributeIndex, boolean useConventionMapping,boolean forMirrorResolution)
    {
        GangAnnotationTypeMapping mapping = this.mapping;

        if (this.useMergedValues) {
            /**
             * 1 method -> basePackageClassses()
             * 2 method -> basePackages()
             * 3 method -> useDefaultFilters
             * */

            int mappedIndex = this.mapping.getAliasMapping(attributeIndex);

            if (mappedIndex == -1 && useConventionMapping) {
                //TODO
            }
            if (mappedIndex != -1){
                /**
                 * 1 method -> basePackageClassses()
                 * */
                mapping = mapping.getRoot();
                attributeIndex = mappedIndex;
            }
        }

        if (!forMirrorResolution) {
            /**
             * 1 method -> basePackageClassses()
             * 2 method -> useDefaultFilters
             * */
        }
        if (attributeIndex == -1) {
            return null;
        }

        if (mapping.getDistance()==0) {
            /**
             * 1 method -> basePackageClassses()
             * */
            Method method = mapping.getAttributes().get(attributeIndex);
            Object result = this.valueExtractor.extract(method, this.rootAttributes);

            return result != null ? result : method.getDefaultValue();
        }

        return getValueFromMetaAnnotation(attributeIndex, forMirrorResolution);
    }

    private Object getValueFromMetaAnnotation(int attributeIndex, boolean forMirrorResolution)
    {
        Object value = null;

        if (this.useMergedValues || forMirrorResolution) {

            /**
             * 1 method -> useDefaultFilters
             * */
            //TODO
        }
        if (value == null){

            Method method = this.mapping.getAttributes().get(attributeIndex);
            value = GangReflectionUtils.invokeMethod(method, this.mapping.getAnnotation());
        }

        return value;
    }

    private Class<?> getTypeForMapOptions(Method attribute, GangAdapt[] adaptations){
        Class<?> attributeType = attribute.getReturnType();
        Class<?> componentType = attributeType.isArray()?attributeType.getComponentType(): attributeType;
        if(GangAdapt.CLASS_TO_STRING.isIn(adaptations) && componentType == Class.class){
            return attributeType.isArray()?String[].class: String.class;
        }
        return Object.class;
    }

    //TODO
    private Object adaptForAttribute(Method attribute,Object value)
    {
        GangClassUtils.resolvePrimitiveIfNecessary(attribute.getReturnType());

        return value;
    }

    //TODO
    private <T> T adapt(Method attribute, Object value, Class<T> type){
        if (value==null){
            return null;
        }

        value = adaptForAttribute(attribute,value);
        type = getAdaptType(attribute,type);
        //TODO
        if (1 ==2){

        }
        else if (value instanceof Class[] && type== String[].class){
            Class<?>[] classes = (Class<?>[]) value;
            String[] names = new String[classes.length];
            for (int i = 0; i < classes.length; i++) {
                names[i] = classes[i].getName();
            }
            value = names;
         }
        return (T) value;
    }

    //TODO
    private <T> Class<T> getAdaptType(Method attribute, Class<T> type){
        if(type != Object.class){
            return type;
        }

        return null;
    }

    /**
     * TODO
     * 业务意义
     * */
    static <A extends Annotation> GangMergedAnnotation<A> of(ClassLoader classLoader,
                                                             Object source,
                                                             Class<A> annotationType,
                                                             Map<String, ?> attributes){

        GangAnnotationTypeMappings mappings = GangAnnotationTypeMappings.forAnnotationType(annotationType);


        return new GangTypeMappedAnnotation<>(mappings.get(0),classLoader,source,attributes,GangTypeMappedAnnotation::extractFromMap,0);
    }

    /**
     * 业务意义
     * 1）反射通过给定方法和参数，获取方法返回值
     * */
    static Object extractFromMap(Method attribute, Object map){
        return map != null ? ((Map<String, ?>) map).get(attribute.getName()) : null;
    }
}
